Remove explicit SSE content encoding#1598
Conversation
Signed-off-by: Dongliang Xie <dragonfsky@gmail.com>
halter73
left a comment
There was a problem hiding this comment.
Sorry. I shouldn't have hastily hit approve here.
Thanks for the spec violation catch, but this header is intentional. ASP.NET Core's own ServerSentEventsServerTransport and Razor Components SSR both still set Content-Encoding: identity to suppress IIS's native DynamicCompressionModule (and as a defense against ResponseCompressionMiddleware if a user adds text/event-stream to their MIME list). IHttpResponseBodyFeature.DisableBuffering() only disables Kestrel-level buffering. it doesn't reach IIS's native compression module, which can sit in front of in-process ANCM workers and will buffer chunks to gzip them, silently breaking SSE streaming. Closing this in favor of keeping the existing behavior. Happy to revisit if you have a concrete client that rejects the header.
- SignalR Core's
ServerSentEventsServerTransport.cs(dotnet/aspnetcore main):**
context.Response.ContentType = "text/event-stream";
context.Response.Headers.CacheControl = "no-cache,no-store";
context.Response.Headers.Pragma = "no-cache";
// Make sure we disable all response buffering for SSE
var bufferingFeature = context.Features.GetRequiredFeature<IHttpResponseBodyFeature>();
bufferingFeature.DisableBuffering();
context.Response.Headers.ContentEncoding = "identity";It still sets the header after DisableBuffering(). They're treated as solving different problems.
src/SignalR/common/Http.Connections/src/Internal/Transports/ServerSentEventsServerTransport.cs
- Razor Components streaming SSR does the same thing, with an explicit comment naming IIS:
// An incomplete QuiescenceTask indicates there may be streaming rendering updates.
// Disable all response buffering and compression on IIS like SignalR's ServerSentEventsServerTransport does.
var bufferingFeature = context.Features.GetRequiredFeature<IHttpResponseBodyFeature>();
bufferingFeature.DisableBuffering();
// ...
context.Response.Headers.ContentEncoding = "identity";What DisableBuffering() does and doesn't cover
| Layer | DisableBuffering() |
X-Accel-Buffering: no |
Content-Encoding: identity |
|---|---|---|---|
| Kestrel response body | ✅ | ❌ | ❌ |
| nginx reverse proxy | ❌ | ✅ | partial |
| IIS DynamicCompressionModule (out-of-process or in-process via ANCM) | ❌ | ❌ | ✅ |
ASP.NET Core ResponseCompressionMiddleware |
❌ | ❌ | ✅ |
The IIS native DynamicCompressionModule runs in the IIS pipeline regardless of ANCM mode, and it skips responses that already have a Content-Encoding header (because they look "already encoded"). When it doesn't skip, it gzips chunks before flushing, which silently breaks SSE streaming. That's the failure mode the header is defending against.
RFC 9110 vs. pragmatics
PR #1598's RFC 9110 §8.4 argument is technically correct (identity is reserved for Accept-Encoding and SHOULD NOT appear in Content-Encoding), but this is exactly the kind of "the wire format is slightly impure to defeat misbehaving intermediaries" choice that the rest of ASP.NET Core has decided is worth it. SignalR Core hasn't removed it despite the same RFC text existing for years.
Motivation and Context
Fixes #1574.
SSE responses currently emit
Content-Encoding: identityeven though no content coding is applied. RFC 9110 section 8.4 definesContent-Encodingas the list of content codings that were applied to the representation and notes thatidentityis reserved forAccept-Encoding, so it SHOULD NOT be included inContent-Encoding.Since no SSE content coding is applied here, the response can omit the header while still preserving the existing SSE buffering controls.
Changes
Content-Encoding: identityassignment from SSE response initialization.Cache-Control,X-Accel-Buffering, andDisableBuffering()behavior.How Has This Been Tested?
dotnet test tests/ModelContextProtocol.AspNetCore.Tests/ModelContextProtocol.AspNetCore.Tests.csproj -f net10.0 --filter "FullyQualifiedName~EventSourceResponse_Includes_ExpectedHeaders"identity.dotnet test tests/ModelContextProtocol.AspNetCore.Tests/ModelContextProtocol.AspNetCore.Tests.csproj -f net10.0 --no-build --no-restoreBreaking Changes
None.
Types of changes
Checklist